import json
import re
from typing import List, Dict
from airflow.hooks.base import BaseHook
from airflow.hooks.http_hook import HttpHook


class PhoneCallHook(BaseHook):
    def __init__(self, conn_id='phone_call'):
        self.http_conn_id = conn_id

    def run(self,
            voice_name: str,
            users: List[Dict[str, str]],
            alert_name: str = '任务告警',
            project: str = '大数据'
            ):
        """
        调用电话通知服务的 HTTP 接口，发送电话通知
        :param voice_name: 电话通知语音内容，不宜超过10字符
        :param users: 电话通知对象，要求格式为 [{ 'username': 'xx', 'phone': '13800000000' }, ...]，`username` 中为 LDAP 账号，需要与 phone 对应
        :param alert_name: 用于电话通知统计的关键词字段，不影响通知内容，一般填"任务超时"
        :param project: 用于电话通知统计的关键词字段，不影响通知内容，一般填"大数据"
        :return:
        """

        # 软校验通知内容长度，未来可能限制在 10 个字以内
        if len(voice_name) > 10:
            print(f'电话通知内容不宜过长：{voice_name}')

        if users is None or len(users) == 0:
            print(f'没有需要拨打的电话')
            return

        # 号码格式校验
        not_match = [u for u in users if not PhoneCallHook._is_valid_phone_number(u['phone'])]
        if len(not_match):
            print('电话号码不符合规则：\n', not_match)
        users = [u for u in users if PhoneCallHook._is_valid_phone_number(u['phone'])]

        if len(users) == 0:
            print(f'没有需要拨打的电话')
            return

        # 号码去重
        users = [{'username': user[0], 'phone': user[1]} for user in
                 set((u['username'], u['phone'])
                     for u in users if isinstance(u, dict)
                     and 'username' in u
                     and isinstance(u['username'], str)
                     and 'phone' in u
                     and isinstance(u['phone'], str))]

        # 向电话通知服务发出鉴权申请，获取 Token
        http = HttpHook(http_conn_id=self.http_conn_id)
        extra = http.get_connection(self.http_conn_id).extra_dejson
        auth_req = {'endpoint': extra.get('authPath') or 'auth/login',
                    'headers' : {'Content-Type': 'application/json'},
                    'data'    : json.dumps({"username": extra.get('username') or 'ylbi',
                                            "password": extra.get('password') or 'Yl@123456'})}
        print('ad auth req:\n', auth_req)
        auth_res = http.run(**auth_req)
        print('ad auth res:\n', auth_res.json())
        token = auth_res.json()['data']['token']

        # 使用 Token 发出电话通知
        http = HttpHook(http_conn_id=self.http_conn_id)
        phone_call_req = {'endpoint': extra.get('path') or 'phone/callphone',
                          'headers' : {'Content-Type' : 'application/json',
                                       'Authorization': f'JWT {token}'},
                          'data'    : json.dumps({"alertCall": {'alertName': alert_name,
                                                                'voiceName': voice_name,
                                                                'project'  : project,
                                                                'user'     : users}})}
        print('phone call req:\n', phone_call_req)
        phone_call_res = http.run(**phone_call_req)
        print('phone call res:\n', phone_call_res.json())

        return phone_call_res

    @classmethod
    def _is_valid_phone_number(cls, phone_number: str) -> bool:
        if not isinstance(phone_number, str):
            return False
        phone_reg = re.compile('^(\+\d{2,3}[ -])?\d{11}$')
        return phone_reg.match(phone_number) is not None
